package com.ftg.midamall.comm.tool;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

/**
 * 类转换以及类转换成树
 *
 * @author qjw
 * @version 1.0
 * @date 2023/10/20 10:44
 */
@SuppressWarnings("all")
public class ConventUtil {

    /**
     * 使用规则 ： 转换的两个类里面的属性一致的才会转换,set方法与get方法同名
     * 静态方法 加载问题 ： 类泛型  是在类实例化的时候调用的
     * 所以我们不能直接在static调用泛型
     *
     * @param targetClass   目标实例，里面一定要有数据
     * @param resultClass   就是一个类型,反射类型
     * @param <ResultClass> 目标类型，当前方法返回值类型
     * @param <TargetClass> 原始数据类型，就是数据里面的某一行的数据
     * @return
     */
    public static <ResultClass, TargetClass> ResultClass conventSwapClass(TargetClass targetClass, Class<ResultClass> resultClass) {
        Object object = null;
        try {
            object = resultClass.newInstance();
            /**
             * getMethods 取到本类当中所有的公共方法和它从父类里面继承的方法
             * getDeclaredMethods 包括公共，保护，默认（包）访问和私有方法，但不包括继承的方法。
             */
            Method[] ms = resultClass.getMethods();

            for (Method m : ms) {
                if (m.getName().startsWith("set")) {
                    String targetMethodName = m.getName().replace("set", "get");

                    try {
                        Method temp = targetClass.getClass().getMethod(targetMethodName);
                        // 我没有对日期类型进行处理  datatime -- entity (localdatatime)
                        m.invoke(object, temp.invoke(targetClass));
                    } catch (NoSuchMethodException ex) {
                        continue;
                    }
                }
            }
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return (ResultClass) object;
    }


    /**
     * 通用工具 类，用来生成树形结构 的
     *
     * @param id               主键名称 ，用来声明树的唯一标识，一般为数据库表主键
     * @param pid              上级节点编号，父节点编号 ，一般为数据库表上级元素  如pid,parent_id ,mgr等
     * @param children         生成树形结构时，相应的节点元素名称
     * @param val              根节点的数值 一般为 -1 , 0 等
     * @param resultList       resultClass目标类，一般为数据里面的数据
     * @param targetClassClass targetClassClass 返回类型
     * @param <ResultClass>
     * @param <TargetClass>
     * @return 树
     */
    public static <ResultClass, TargetClass> List<TargetClass> createTree(String id, String pid, String children, long val, List<ResultClass> resultList, Class<TargetClass> targetClassClass) {
        //这个就是我们要用到的getPid方法名称
        String pidMethodName = methodNameBySetterGetter(pid, SetGetEnum.GET_TYPE_ENUM);

        List<TargetClass> list = new ArrayList<>();
        for (ResultClass result : resultList) {
            long pidVal = 0;
            try {
                Method tempMethod = result.getClass().getMethod(pidMethodName);
                Object tempVal = tempMethod.invoke(result);
                pidVal = (long) tempVal;
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                System.out.println("pid没有，或者pid参数名称传递错误");
                e.printStackTrace();
            }
            if (pidVal == val) {
                TargetClass vo = ConventUtil.conventSwapClass(result, targetClassClass);
                list.add(vo);
                currentNodeChildren(id, pid, children, resultList, vo);
            }
        }
        return list;
    }

    /**
     * 当前节点currentNode，查找所有子节点，并递归求所有无级节点
     *
     * @param id            主键名称 ，用来声明树的唯一标识，一般为数据库表主键
     * @param pid           上级节点编号，父节点编号 ，一般为数据库表上级元素  如pid,parent_id ,mgr等
     * @param children      生成树形结构时，相应的节点元素名称
     * @param resultList    resultClass目标类，一般为数据里面的数据
     * @param currentNode   当前节点
     * @param <ResultClass>
     * @param <TargetClass>
     */
    public static <ResultClass, TargetClass> void currentNodeChildren(String id, String pid, String children, List<ResultClass> resultList, TargetClass currentNode) {

        String pidGetMethodName = methodNameBySetterGetter(pid, SetGetEnum.GET_TYPE_ENUM);
        String idGetMethodName = methodNameBySetterGetter(id, SetGetEnum.GET_TYPE_ENUM);
        String childrenGetMethodName = methodNameBySetterGetter(children, SetGetEnum.GET_TYPE_ENUM);
        String childrenSetMethodName = methodNameBySetterGetter(children, SetGetEnum.SET_TYPE_ENUM);

        for (ResultClass resultClass : resultList) {
            //当前节点的id
            long idVal = 0;
            //resultClass 的
            //求出当前currentNode的id
            long resultClassPidVal = 0;
            try {
                Method idGetMethod = currentNode.getClass().getMethod(idGetMethodName);
                idVal = (long) idGetMethod.invoke(currentNode);

                Method resultClassPidGetMethod = resultClass.getClass().getMethod(pidGetMethodName);
                Object pidObject = resultClassPidGetMethod.invoke(resultClass);
                ;
                resultClassPidVal = Long.valueOf(pidObject.toString());

                if (idVal == resultClassPidVal) {
                    Method childrenGetMethod = currentNode.getClass().getMethod(childrenGetMethodName);
                    List<TargetClass> childrenList = (List<TargetClass>) childrenGetMethod.invoke(currentNode);
                    if (Objects.isNull(childrenList)) {
                        Method childrenSetMethod = currentNode.getClass().getMethod(childrenSetMethodName, List.class);
                        childrenSetMethod.invoke(currentNode, new ArrayList<>(1));
                    }

                    List<TargetClass> childrenSecondList = (List<TargetClass>) childrenGetMethod.invoke(currentNode);
                    TargetClass o = (TargetClass) ConventUtil.conventSwapClass(resultClass, currentNode.getClass());
                    childrenSecondList.add(o);
                }


            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }

        Method childrenGetMethod = null;
        try {
            childrenGetMethod = currentNode.getClass().getMethod(childrenGetMethodName);
            List<TargetClass> childrenList = (List<TargetClass>) childrenGetMethod.invoke(currentNode);
            if (childrenList == null || childrenList.isEmpty()) {
                return;
            }
            for (TargetClass newSon : childrenList) {
                currentNodeChildren(id, pid, children, resultList, newSon);
            }

        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }


    }

    /**
     * 内部类用来完成对我们setter和getter方式的判断
     */
    private enum SetGetEnum {
        /**
         * set类型
         */
        SET_TYPE_ENUM,
        /**
         * get类型
         */
        GET_TYPE_ENUM
    }

    /**
     * 根据传递过来的属性名称 ，来动态生成相庆的setter与getter方法
     *
     * @param filedName 字段
     * @param typeEnum  类型  SetGetEnum
     * @return
     */
    private static String methodNameBySetterGetter(String filedName, SetGetEnum typeEnum) {
        String prefix = "";
        if (typeEnum == SetGetEnum.SET_TYPE_ENUM) {
            prefix = "set";
        }

        if (typeEnum == SetGetEnum.GET_TYPE_ENUM) {
            prefix = "get";
        }
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(prefix).append(filedName.substring(0, 1).toUpperCase(Locale.ROOT)).append(filedName.substring(1));
        return stringBuilder.toString();
    }

    /**
     * 为了解决当前系统当中，固化结构而设计
     *
     * @param resultList       数据里面的数据
     * @param targetClassClass 树的类型
     * @param <ResultClass>    数据里面的数据
     * @param <TargetClass>    树的类型
     * @return
     */
    public static <ResultClass, TargetClass> List<TargetClass> createTree(List<ResultClass> resultList, Class<TargetClass> targetClassClass) {
        return createTree("id", "parentId", "children", -1, resultList, targetClassClass);
    }
}
